package top.zxybird.yun.serialization.thrift;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import org.springframework.beans.BeanUtils;
import org.springframework.util.CollectionUtils;

import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;

import top.zxybird.yun.serialization.thrift.model.TStruct;
import top.zxybird.yun.serialization.thrift.model.TType;

public class TType2Object {

  @SuppressWarnings("unchecked")
  public static <T> T transform(TType in, Class<T> clazz) {

    if (null == in || null == in.getValue()) {
      return null;
    }

    Preconditions.checkNotNull(clazz);

    if (clazz == Byte.class || clazz == byte.class) {
      // Preconditions.checkArgument(in.getValue().getClass() == clazz);
      return (T) in.getValue();
    }
    if (clazz == Boolean.class || clazz == boolean.class) {
      // Preconditions.checkArgument(in.getValue().getClass() == clazz);
      return (T) in.getValue();
    }
    if (clazz == Short.class || clazz == short.class) {
      // Preconditions.checkArgument(in.getValue().getClass() == clazz);
      return (T) in.getValue();
    }
    if (clazz == Integer.class || clazz == int.class) {
      // Preconditions.checkArgument(in.getValue().getClass() == clazz);
      return (T) in.getValue();
    }
    if (clazz == Long.class || clazz == long.class) {
      // Preconditions.checkArgument(in.getValue().getClass() == clazz);
      return (T) in.getValue();
    }
    if (clazz == Double.class || clazz == double.class) {
      // Preconditions.checkArgument(in.getValue().getClass() == clazz);
      return (T) in.getValue();
    }
    if (clazz == String.class) {
      // Preconditions.checkArgument(in.getValue().getClass() == clazz);
      return (T) in.getValue();
    }

    // 是个struct
    if (null == in.getValue()) {
      return null;
    }

    TStruct struct = (TStruct) in;
    List<TType> typeList = struct.getAllField();
    if (CollectionUtils.isEmpty(typeList)) {
      return null;
    }

    // TType中的参数列表
    Map<Short, TType> typeMap = typeList.stream().filter(Objects::nonNull)
        .collect(Collectors.toMap(TType::getNumber, x -> x, (o1, o2) -> o1));

    // TODO 此处可以cache一下
    List<PropertyDescriptor> propertyDescriptorList = Arrays.stream(BeanUtils.getPropertyDescriptors(clazz))
        .filter(x -> x.getPropertyType() != Class.class).collect(Collectors.toList());

    Map<Short, PropertyDescriptor> propertyDescriptorMap = Maps.newHashMap();

    IntStream.range(0, propertyDescriptorList.size()).boxed()
        .forEach(index -> propertyDescriptorMap.put((short) (index + 1), propertyDescriptorList.get(index)));

    T t;
    try {
      t = clazz.newInstance();
    } catch (Exception e) {
      throw new RuntimeException(
          String.format("Can not instance class %s by default constructor", clazz.getSimpleName()));
    }

    typeMap.forEach((index, value) -> {

      PropertyDescriptor p = propertyDescriptorMap.get(index);
      Class fieldClazz = p.getPropertyType();

      Object obj = null;

      try {
        obj = transform(value, fieldClazz);
      } catch (Exception e) {
        e.printStackTrace();
      }
      Method writeMethod = p.getWriteMethod();
      try {
        writeMethod.invoke(t, obj);
      } catch (Exception e) {
        e.printStackTrace();
      }

    });

    return t;
  }

}
